//
// Created by HP on 2022/1/3.
//

#include "lve_window.h"
#include <stdexcept>
#include <iostream>
#include <utility>

namespace lve {

    LveWindow::LveWindow(int w, int h, std::string name): width{w}, height{h}, windowName{std::move(name)} {
        initWindow();
    }

    LveWindow::~LveWindow() {
        glfwDestroyWindow(window);
        glfwTerminate();
    }

    void LveWindow::initWindow() {
        glfwInit();

        glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
        glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);

        window = glfwCreateWindow(width, height, windowName.c_str(), nullptr, nullptr);
        glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
        glfwSetWindowUserPointer(window, this);
        glfwSetFramebufferSizeCallback(window, framebufferResizedCallback);
        glfwSetCursorEnterCallback(window, cursorEnterCallback);
        glfwSetMouseButtonCallback(window, mouseButtonInputCallback);
        glfwSetKeyCallback(window, keyboardInputCallback);
    }

    void LveWindow::createWindowSurface(VkInstance instance, VkSurfaceKHR *surface) {
        if(glfwCreateWindowSurface(instance, window, nullptr, surface) != VK_SUCCESS) {
            throw std::runtime_error{"failed to create window surface"};
        }
    }

    void LveWindow::framebufferResizedCallback(GLFWwindow *window, int width, int height) {
        auto lveWindow = reinterpret_cast<LveWindow * >(glfwGetWindowUserPointer(window));
        lveWindow->framebufferResized = true;
        lveWindow->width = width;
        lveWindow->height = height;
    }

    void LveWindow::cursorEnterCallback(GLFWwindow *window, int entered) {
        auto* lveWindowPtr = reinterpret_cast<LveWindow*>(glfwGetWindowUserPointer(window));
        lveWindowPtr->isEnter = entered;
    }

    void LveWindow::getCursorPos(double *xPos, double *yPos) {
        if (isEnter) {
            glfwGetCursorPos(window, xPos, yPos);
            *xPos /= static_cast<double>(width);
            *xPos *= 2.f;
            *xPos -= 1.f;
            *yPos /= static_cast<double>(height);
            *yPos *= 2.f;
            *yPos -= 1.f;
        } else {
            *xPos = 0.f;
            *yPos = 0.f;
        }
    }

    void LveWindow::mouseButtonInputCallback(GLFWwindow *window, int button, int action, int mods) {
        auto* lveWindowPtr = reinterpret_cast<LveWindow*>(glfwGetWindowUserPointer(window));
        if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS && lveWindowPtr->isEnter) {
            glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN | GLFW_CURSOR_DISABLED);
        }
    }

    void LveWindow::keyboardInputCallback(GLFWwindow *window, int key, int scancode, int action, int mods) {
        auto* lveWindowPtr = reinterpret_cast<LveWindow*>(glfwGetWindowUserPointer(window));
        if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) {
            lveWindowPtr->isEnter = false;
            glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
        }
    }

}